<!-- Table 文档: https://juejin.cn/post/7166068828202336263 -->
<template>
  <!-- 表格主体 -->
  <div class="my-table" style="height: 100%" v-loading="loading" element-loading-text="数据加载中……">
    <!-- 操作按钮和表格 -->
    <div class="my-table-main" :style="{ height: tableHeight }">
      <!-- 表格主体 -->
      <el-table
        ref="tableRef"
        :="$attrs"
        :data="processTableData"
        :border="border"
        :stripe="stripe"
        :row-key="rowKey"
        height="100%"
        :cell-style="{ textAlign: 'center' }"
        :header-cell-style="{ 'text-align': 'center' }"
        @selection-change="selectionChange"
      >
        <!-- 默认插槽 -->
        <slot></slot>

        <!-- 插入表格最后一行之后的插槽 -->
        <template #append>
          <slot name="append"></slot>
        </template>
        <!-- 无数据 -->
        <template #empty>
          <div class="table-empty">
            <slot name="empty">
              <img src="@/assets/images/notData.png" alt="notData" />
              <div>暂无数据</div>
            </slot>
          </div>
        </template>
      </el-table>
    </div>
    <!-- 分页组件和工具按钮 -->
    <div class="my-table-page" v-if="pagination || toolButton">
      <div class="my-table-pagination">
        <Pagination
          v-if="pagination"
          :pageable="pageable"
          :handle-size-change="handleSizeChange"
          :handle-current-change="handleCurrentChange"
        />
      </div>
      <div v-if="toolButton" class="my-table-tool">
        <slot name="toolButton">
          <el-button v-if="showToolButton('refresh')" icon="el-icon-refresh" circle @click="getTableList" />
        </slot>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts" name="Table">
import { ref, watch, provide, onMounted, unref, computed } from 'vue'
import { ElTable } from 'element-plus'
import { useTable } from '@/hooks/useTable'
import { useSelection } from '@/hooks/useSelection'
import { ColumnProps, TypeProps } from '@/components/Table/interface'
import Pagination from './components/Pagination.vue'
import Sortable from 'sortablejs'

export interface ITableProps {
  columns?: ColumnProps[] // 列配置项  ==> 非必传
  title?: string // 表格标题 ==> 非必传
  data?: any[] // 静态 table data 数据，若存在则不会使用 requestApi 返回的 data ==> 非必传
  initParam?: any // 初始化请求参数 ==> 非必传（默认为{}）
  requestApi?: (params: any) => Promise<any> // 请求表格数据的 api ==> 非必传
  requestAuto?: boolean // 是否自动执行请求 api ==> 非必传（默认为true）
  requestError?: (params: any) => void // 表格 api 请求错误监听 ==> 非必传
  dataCallback?: (data: any) => any // 返回数据的回调函数，可以对数据进行处理 ==> 非必传
  pagination?: boolean // 是否需要分页组件 ==> 非必传（默认为true）
  border?: boolean // 是否带有纵向边框 ==> 非必传（默认为false）
  stripe?: boolean // 是否带有斑马纹 ==> 非必传（默认为true）
  toolButton?: ('refresh' | 'setting' | 'search')[] | boolean // 是否显示表格功能按钮 ==> 非必传（默认为true）
  rowKey?: string // 行数据的 Key，用来优化 Table 的渲染，当表格数据多选时，所指定的 id ==> 非必传（默认为 id）
}

// 接受父组件参数，配置默认值
const props = withDefaults(defineProps<ITableProps>(), {
  columns: () => [],
  requestAuto: true,
  pagination: true,
  initParam: {},
  border: false,
  stripe: true,
  toolButton: true,
  rowKey: 'id',
  searchCol: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 })
})
// 表格加载状态
const loading = ref<boolean>(false)

// table 实例
const tableRef = ref<InstanceType<typeof ElTable>>()

// column 列类型
const columnTypes: TypeProps[] = ['selection', 'radio', 'index', 'expand', 'sort']

// 表格高度
const tableHeight = computed(() => (props.pagination || props.toolButton ? 'calc(100% - 50px)' : '100%'))

// 控制 ToolButton 显示
const showToolButton = (key: 'refresh' | 'setting' | 'search') => {
  return Array.isArray(props.toolButton) ? props.toolButton.includes(key) : props.toolButton
}

// 单选值
const radio = ref('')

// 表格多选 Hooks
const { selectionChange, selectedList, selectedListIds, isSelected } = useSelection(props.rowKey)

// 表格操作 Hooks
const {
  tableData,
  pageable,
  searchParam,
  searchInitParam,
  getTableList,
  search,
  reset,
  handleSizeChange,
  handleCurrentChange
} = useTable(props.requestApi, props.initParam, props.pagination, props.dataCallback, props.requestError, loading)

// 清空选中数据列表
const clearSelection = () => tableRef.value!.clearSelection()

// 初始化表格数据 && 拖拽排序
onMounted(() => {
  dragSort()
  props.requestAuto && getTableList()
  props.data && (pageable.value.total = props.data.length)
})

// 处理表格数据
const processTableData = computed(() => {
  if (!props.data) return tableData.value
  if (!props.pagination) return props.data
  return props.data.slice(
    (pageable.value.pageNum - 1) * pageable.value.pageSize,
    pageable.value.pageSize * pageable.value.pageNum
  )
})

// 监听页面 initParam 改化，重新获取表格数据
watch(() => props.initParam, getTableList, { deep: true })

// 定义 enumMap 存储 enum 值（避免异步请求无法格式化单元格内容 || 无法填充搜索下拉选择）
const enumMap = ref(new Map<string, { [key: string]: any }[]>())
const setEnumMap = async ({ prop, enum: enumValue }: ColumnProps) => {
  if (!enumValue) return

  // 如果当前 enumMap 存在相同的值 return
  if (enumMap.value.has(prop!) && (typeof enumValue === 'function' || enumMap.value.get(prop!) === enumValue)) return

  // 当前 enum 为静态数据，则直接存储到 enumMap
  if (typeof enumValue !== 'function') return enumMap.value.set(prop!, unref(enumValue!))

  // 为了防止接口执行慢，而存储慢，导致重复请求，所以预先存储为[]，接口返回后再二次存储
  enumMap.value.set(prop!, [])

  // 当前 enum 为后台数据需要请求数据，则调用该请求接口，并存储到 enumMap
  const { data } = await enumValue()
  enumMap.value.set(prop!, data)
}

// 注入 enumMap
provide('enumMap', enumMap)

// 列设置 ==> 需要过滤掉不需要设置的列
const colRef = ref()
const openColSetting = () => colRef.value.openColSetting()

// 定义 emit 事件
const emit = defineEmits<{
  search: []
  reset: []
  dargSort: [{ newIndex?: number; oldIndex?: number }]
}>()

// 拖拽排序
const dragSort = () => {
  const tbody = document.querySelector('.el-table__body-wrapper tbody') as HTMLElement
  Sortable.create(tbody, {
    handle: '.move',
    animation: 300,
    onEnd({ newIndex, oldIndex }) {
      const [removedItem] = processTableData.value.splice(oldIndex!, 1)
      processTableData.value.splice(newIndex!, 0, removedItem)
      emit('dargSort', { newIndex, oldIndex })
    }
  })
}

// 暴露给父组件的参数和方法 (外部需要什么，都可以从这里暴露出去)
defineExpose({
  element: tableRef,
  tableData: processTableData,
  radio,
  pageable,
  searchParam,
  searchInitParam,
  getTableList,
  search,
  reset,
  handleSizeChange,
  handleCurrentChange,
  clearSelection,
  enumMap,
  isSelected,
  selectedList,
  selectedListIds
})
</script>
<style lang="scss" scoped>
.my-table-main {
  height: calc(100% - 50px);
  // 表格 header 样式
  .my-table-header {
    .header-button-lf {
      float: left;
    }
    .header-button-ri {
      float: right;
    }
    .el-button {
      margin-bottom: 15px;
    }
  }
}
.my-table-page {
  height: 50px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 15px;
}
.my-table-tool {
  white-space: nowrap;
}
.my-table {
  &:deep(.el-table__footer) {
    .cell {
      font-weight: bold;
    }
  }
  &:deep(.el-table__body-wrapper) {
    .el-scrollbar__bar {
      &.is-horizontal {
        height: 12px;
        border-radius: 12px;
      }
      &.is-vertical {
        width: 12px;
        border-radius: 12px;
      }
    }
  }
}
</style>
